import { IRVOArrayItem } from "./RVOArrayEx";
import { RvoPool, IRvoPoolItem } from "./RvoPool";

/*
 * Vector2.h
 * RVO2 Library
 *
 * SPDX-FileCopyrightText: 2008 University of North Carolina at Chapel Hill
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Please send all bug reports to <geom@cs.unc.edu>.
 *
 * The authors may be contacted via:
 *
 * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
 * Dept. of Computer Science
 * 201 S. Columbia St.
 * Frederick P. Brooks, Jr. Computer Science Bldg.
 * Chapel Hill, N.C. 27599-3175
 * United States of America
 *
 * <https://gamma.cs.unc.edu/RVO2/>
 */
export const RVO_EPSILON = 0.00001;
/**
 * @brief Defines a two-dimensional vector.
 */
export class Vector2 implements IRvoPoolItem, IRVOArrayItem {
  static vector1: Vector2 = new Vector2;
  static vector2: Vector2 = new Vector2;
  static vector3: Vector2 = new Vector2;

  static pool: RvoPool<Vector2> = new RvoPool(Vector2, 256);

  rvoIndex: number;
  inPool: boolean;
  
  x_: number;
  y_: number;
  /**
   * @brief     Constructs and initializes a two-dimensional vector from the
   *            specified xy-coordinates.
   * @param[in] x The x-coordinate of the two-dimensional vector.
   * @param[in] y The y-coordinate of the two-dimensional vector.
   */
  constructor(x: number = 0, y: number = 0) {
    this.x_ = x;
    this.y_ = y;
  }

  reuse(): void {
    this.x_ = 0;
    this.y_ = 0;
  }

  unuse(): void {

  }

  set(vector: Vector2 | number, y?: number) {
    if (vector instanceof Vector2) {
      this.x_ = vector.x_;
      this.y_ = vector.y_;
    } else {
      this.x_ = vector;
      this.y_ = y;
    }
    return this;
  }

  add(vector: Vector2, out?: Vector2) {
    out = out || this;
    out.x_ = this.x_ + vector.x_;
    out.y_ = this.y_ + vector.y_;
    return out;
  }

  sub(vector: Vector2, out?: Vector2) {
    out = out || this;
    out.x_ = this.x_ - vector.x_;
    out.y_ = this.y_ - vector.y_;
    return out;
  }

  mul(vector: Vector2, out?: Vector2) {
    out = out || this;
    out.x_ = this.x_ * vector.x_;
    out.y_ = this.y_ * vector.y_;
    return out;
  }

  mulScalar(scalar: number, out?: Vector2) {
    out = out || this;
    out.x_ = this.x_ * scalar;
    out.y_ = this.y_ * scalar;
    return out;
  }

  div(vector: Vector2, out?: Vector2) {
    out = out || this;
    out.x_ = this.x_ / vector.x_;
    out.y_ = this.y_ / vector.y_;
    return out;
  }

  divScalar(scalar: number, out?: Vector2) {
    out = out || this;
    out.x_ = this.x_ / scalar;
    out.y_ = this.y_ / scalar;
    return out;
  }

  dot(vector: Vector2) {
    return this.x_ * vector.x_ + this.y_ * vector.y_;
  }

  normalize(out?: Vector2) {
    return this.divScalar(Vector2.abs(this), out);
  }

  clone() {
    return new Vector2(this.x_, this.y_);
  }

  absSq() {
    return this.dot(this);
  }

  abs() {
    return Math.sqrt(this.absSq());
  }
  /**
   * @relates   Vector2
   * @brief     Computes the length of a specified two-dimensional vector.
   * @param[in] vector The two-dimensional vector whose length is to be computed.
   * @return    The length of the two-dimensional vector.
   */
  static abs(vector: Vector2) {
    return Math.sqrt(vector.dot(vector));
  }

  /**
   * @relates   Vector2
   * @brief     Computes the squared length of a specified two-dimensional vector.
   * @param[in] vector The two-dimensional vector whose squared length is to be
   *                   computed.
   * @return    The squared length of the two-dimensional vector.
   */
  static absSq(vector: Vector2) {
    return vector.dot(vector);
  }

  /**
   * @relates   Vector2
   * @brief     Computes the determinant of a two-dimensional square matrix with
   *            rows consisting of the specified two-dimensional vectors.
   * @param[in] vector1 The top row of the two-dimensional square matrix.
   * @param[in] vector2 The bottom row of the two-dimensional square matrix.
   * @return    The determinant of the two-dimensional square matrix.
   */
  static det(vector1: Vector2, vector2: Vector2) {
    return vector1.x_ * vector2.y_ - vector1.y_ * vector2.x_;
  }

  /**
   * @brief     Computes the signed distance from a line connecting th specified
   *            points to a specified point.
   * @param[in] vector1 The first point on the line.
   * @param[in] vector2 The second point on the line.
   * @param[in] vector3 The point to which the signed distance is to be
   *                    calculated.
   * @return    Positive when the point vector3 lies to the left of the line
   *            vector1-vector2.
   */
  static leftOf(vector1: Vector2, vector2: Vector2, vector3: Vector2) {
    return this.det(vector1.sub(vector3, Vector2.vector1), vector2.sub(vector1, Vector2.vector2));
  }
} 
